/*
连接服务端websocket，监听服务端消息，进行控制LED
需要安装的库：
  1、WebSockets by Markus
  2、WiFiManager by tzapu
*/
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include <WiFiManager.h>
#include <Ticker.h>

// GPIO#0 is for Adafruit ESP8266 HUZZAH board. Your board LED might be on 13.
const int LEDPIN = 2;
const int RELAYPIN = 0;
// Current LED status
bool LEDStatus = false;

// websocker服务器连接情况
bool isConfigWebsocket = false;

// wifi网络连接情况
bool wifiIsConnected = false;

// Commands sent through Web Socket
const char LEDON[] = "on";
const char LEDOFF[] = "off";

// WebSocket服务器IP地址
String remoteWebSockerServerHost = "22q376j554.oicp.vip";
int socketPort = 8090;
String chipId = (String)ESP.getChipId();
String remoteWebSocketUri = "/api/websocket/iot/" + chipId;
// 热点名称
String apName = "ESP-" + chipId;

static void writeLED(bool);

//初始化websocket客户端
WebSocketsClient wsClient;
Ticker ticker;

void setup() {
  // put your setup code here, to run once:
  pinMode(LEDPIN, OUTPUT);
  pinMode(RELAYPIN, OUTPUT);
  writeLED(false);

  Serial.begin(9600);

  // 连接WiFi
  connectWiFi();
  // 初始化websocket连接
  initWebSocketClient();
}

void loop() {
  // put your main code here, to run repeatedly:

  wsClient.loop();
}

// 自定义方法区
// 连接WiFi
void connectWiFi() {

  // 异步任务，每100ms执行一次LED呼吸
  ticker.attach_ms(500, wifiConnectBreath);

  // 建立WiFiManager对象
  WiFiManager wifiManager;
  // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
  // wifiManager.resetSettings();

  // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
  Serial.print(apName.c_str());

  // 尝试连接网络，失败去到配置页面
  wifiManager.autoConnect(apName.c_str(), "12345678");
  //等待WiFi连接,连接成功打印IP
  wifiIsConnected = true;
  Serial.print("WiFi Connected!");
}
// 初始化websocket客户端
// https://blog.csdn.net/yq543858194/article/details/109186301?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167328186316800188599931%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167328186316800188599931&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-109186301-null-null.142^v70^one_line,201^v4^add_ask&utm_term=ESP8266%E8%BF%9E%E6%8E%A5websocket&spm=1018.2226.3001.4187
void initWebSocketClient() {

  // 启动WebSocket客户端
  Serial.print("初始化websocket客户端");
  Serial.print("请求host ==》");
  Serial.println(remoteWebSockerServerHost);

  // 获取chipId
  String theChipId = (String)ESP.getChipId();
  Serial.println(theChipId);
  // 转为十六进制
  Serial.printf(" ESP8266 Chip id = %08X\n", ESP.getChipId());
  Serial.print("请求uri==》");
  Serial.println(remoteWebSocketUri);

  wsClient.begin(remoteWebSockerServerHost, socketPort, remoteWebSocketUri);

  // 指定事件处理函数
  wsClient.onEvent(websocketEvent);
}

void websocketEvent(WStype_t type, uint8_t *payload, size_t length) {
  Serial.printf("webSocketEvent ===>(%d, %d, ...)\r\n", length, type);
  switch (type) {
    case WStype_ERROR:
      LEDStatus = false;
      Serial.printf("[%u] WStype_ERROR，WebSocket连接发生错误时触发!\r\n", WStype_ERROR);
      break;
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected 客户端或服务端关闭连接时触发!\r\n", WStype_DISCONNECTED);
      LEDStatus = false;
      // websocket连接状态呼吸灯
      websocketWarningBreath();
      break;
    case WStype_CONNECTED:
      Serial.printf("[%u] WStype_CONNECTED 客户端连接到服务端时触发!\r\n", WStype_CONNECTED);
      // writeLED(false);
      deviceSign();
      break;
    case WStype_TEXT:
      Serial.printf("[%u] get Text 接收到文本数据时触发: %s\r\n", length, payload);

      if (strcmp(LEDON, (const char *)payload) == 0) {
        writeLED(true);
      } else if (strcmp(LEDOFF, (const char *)payload) == 0) {
        writeLED(false);
      } else {
        Serial.println("Unknown command");
      }
      break;
    case WStype_BIN:
      Serial.printf("[%u] WStype_BIN 接收到二进制数据时触发!\r\n", WStype_CONNECTED);
      break;
    case WStype_PING:
      // Ping和Pong是websocket里的心跳，用来保证客户端是在线的，服务端给客户端发送PING，客户端发送PONG来回应
      Serial.printf("[%u] WStype_PING !\r\n", WStype_CONNECTED);
      break;
    case WStype_PONG:
      Serial.printf("[%u] WStype_PONG!\r\n", WStype_CONNECTED);
      break;
    default:
      Serial.printf("Invalid WStype [%d]\r\n", type);
      break;
  }
}

// 设备签到
void deviceSign() {

  String deviceMac = WiFi.macAddress();
  Serial.println("字符串获取MAC地址: " + deviceMac);
  String deviceIp = WiFi.localIP().toString();

  Serial.println("字符串获取IP地址: " + deviceIp);
  // 将设备的当前信息发送到服务器
  String msg = "{\"deviceMac\":\"" + deviceMac + "\",\"deviceId\":\"" + chipId + "\",\"ledStatus\":\"" + LEDStatus + "\",\"deviceIp\":\"" + deviceIp + "\"}";
  wsClient.sendTXT(msg);
}

// 点灯
static void writeLED(bool LEDon) {
  LEDStatus = LEDon;
  // Note inverted logic for Adafruit HUZZAH board
  if (LEDon) {
    digitalWrite(LEDPIN, 0);
    digitalWrite(RELAYPIN, 0);
  } else {
    // 关闭
    digitalWrite(LEDPIN, 1);
    digitalWrite(RELAYPIN, 1);
  }
}

// websocket连接失败，则一直慢闪呼吸灯
void websocketWarningBreath() {
  // 如果websocket服务器连接失败，则一直闪灯
  int val = digitalRead(LEDPIN);

  if (val == LOW) {
    digitalWrite(LEDPIN, 1);
    delay(1000);
  } else {
    digitalWrite(LEDPIN, 0);
    delay(3000);
  }
}

// wifi如果未连接，则一直快闪led
void wifiConnectBreath() {
  Serial.println("wifiConnectBreath");

  int val = digitalRead(LEDPIN);

  // 检查引脚2是否为低电平
  if (val == LOW) {
    digitalWrite(LEDPIN, 1);
  } else {
    digitalWrite(LEDPIN, 0);
  }
  Serial.print("Wi-Fi连接状态");

  Serial.println(wifiIsConnected);
  if (wifiIsConnected) {
    ticker.detach();  // 使用detach来停止ticker对象定时调用函数
  }
}
